from block import *
from model import *
from marioActor import Mario
from types import ListType
from item import *
from pandac.PandaModules import Texture
from pandac.PandaModules import TextureStage
'''
	Represents one cell in the space partition
	and all the items, enemies, blocks, etc that 
	it can contain
'''
class MapCell:
	'''
		Creates a map cell with a list of enemies that are currently in
		that cell, the type of block in the cell, and any items in the cell
		
		Params: block -- the block that maintains this space
				enemies -- either an empty list or the first enemy in the cell
				items -- either an empty list or the first item in the cell
	'''
	def __init__(self, block, enemies=None, items=None):
		self.enemies = []
		self.items = []
		if (enemies != None):
			self.enemies.append(enemies)
		
		if (items != None):
			self.items.append(items)
			
		self.block = block
		
'''
	Andrew Oberlin
	Date: February 8, 2012
	
	Class for loading a level
'''
class LevelManager:
	HEIGHT = 'HEIGHT'
	WIDTH = 'WIDTH'
	'''
		Constructs an instance of the level loader
	'''
	def __init__(self, parent):
		self.parent = parent
		self.level_dir = "models/levels/"
		self.enemies_dir = "models/characters/enemies/"
		self.isHidden = True
		self.mario = None
		self.map = None
		self.backgroundModel = self.parent.loader.loadModelCopy("models/plane")
	
	'''
		Loads the background
	'''
	def __loadBackground(self):
		self.backgroundTex = self.parent.loader.loadTexture("models/levels/objects/background.png")
		self.backgroundModel = self.parent.loader.loadModelCopy("models/plane")
		self.backgroundModel.setTexture(self.backgroundTex)
		
		self.backgroundTex.setWrapU(Texture.WMRepeat)
		self.backgroundTex.setWrapV(Texture.WMRepeat)
		
	
	'''
		Sets the properties for a two dimensional figure
	'''
	def __set2DProps(self, maxWidth, maxHeight):
		self.backgroundModel.setHpr(Vec3(0,-90,0))
		self.backgroundModel.setScale(maxWidth*self.parent.BLOCK_SIZE, maxHeight*self.parent.BLOCK_SIZE, self.parent.WIN_HEIGHT)
		self.backgroundModel.setPos(maxWidth*self.parent.BLOCK_SIZE/2, maxHeight*self.parent.BLOCK_SIZE/2,0)
		self.backgroundModel.setTexScale(TextureStage.getDefault(), maxWidth/ 20, 1)
		

		# maximize efficiency
		self.backgroundModel.setBin("unsorted", 0)
		self.backgroundModel.setDepthTest(False)
		self.backgroundModel.setTransparency(1)

		self.backgroundModel.reparentTo(render)
	
	'''
		Marks the level as hidden
	'''
	def hide(self):
		self.isHidden = True
		if (self.map != None):
			for column in self.map:
				for row in column:
					for enemy in row.enemies:
						enemy.model.hide()
						
					if (row.block.type != 'AIR'):
						row.block.model.hide()
					
		if (self.mario != None):
			self.mario.model.hide()
		self.backgroundModel.hide()
	'''
		Marks the level as being shown on the screen
	'''
	def show(self):
		self.isHidden = False
		if (self.map != None):
			for column in self.map:
				for row in column:
					for enemy in row.enemies:
						enemy.model.show()
					
					if (row.block.type != 'AIR'):	
						row.block.model.show()
		if (self.mario != None):			
			self.mario.model.show()
		self.backgroundModel.show()
	
	'''
		Reads in a hard file that represents a block of space in the game
		Made for faster level design
		
		filename -- the filename
	'''
	def loadLevel(self, name):
		level_file = open(self.level_dir + name + '.lvl', 'r')
		(MAX_WIDTH, MAX_HEIGHT) = self.__findLevelAttr(level_file)
		
		self.__loadBackground()
		self.__set2DProps(MAX_WIDTH, MAX_HEIGHT-1)
		
		if (self.map != None):
			self.destroyMap()
		self.map = [[None for i in range(MAX_WIDTH)] for j in range(MAX_HEIGHT)]
				
		for i in range(MAX_HEIGHT - 1, -1, -1):
			line = level_file.readline()
			for j in range(len(line)):
				if (line[j] == 'M'):
					if (self.mario == None):
						self.mario = Mario(self.parent)
					self.mario.model.setPos(
						Vec3((j + 0.5)*self.parent.BLOCK_SIZE, (i + 0.5)*self.parent.BLOCK_SIZE,0)
					)
					self.mario.model.show()
				self.map[i][j] = self.__getCellModel(line[j], i, j)
				# sets the correct position for the block (but no model for air so do not call it)
				if (self.map[i][j].block.type != 'AIR'):
					self.map[i][j].block.model.setPos(
						Vec3((j + 0.5)*self.parent.BLOCK_SIZE, (i + 0.5)*self.parent.BLOCK_SIZE,0)
					)
					self.map[i][j].block.setOriginalY()
					
				# the map has a list of enemies that are in this cell of the map
				# this will allow us to have multiple enemies in one block
				
				for enemy in self.map[i][j].enemies:
					enemy.model.setPos(
						Vec3((j + 0.5)*self.parent.BLOCK_SIZE, (i + 0.5)*self.parent.BLOCK_SIZE,0)
					)
					
				for item in self.map[i][j].items:
					item.model.setPos(
						Vec3((j + 0.5)*self.parent.BLOCK_SIZE, (i + 0.5)*self.parent.BLOCK_SIZE,0)
					)
				
			for j in range(len(line), MAX_WIDTH):
				self.map[i][j] = self.__getCellModel(' ')
	
		
		self.width = MAX_WIDTH*self.parent.BLOCK_SIZE
		self.height = MAX_HEIGHT*self.parent.BLOCK_SIZE
		self.mario.model.reparentTo(render);
		self.parent.gameStats.coinDummy.reparentTo(render);
		self.parent.gameStats.livesDummy.reparentTo(render);
		self.show()
		self.mario.alive = True
		self.mario.win = False
		
		print "After loading: " + str(self.mario.model.getPos())
		
		
	'''
		Creates the correct type of block to put in the map
		
		Params: character -- the character from the file to be mapped
	'''
	def __getCellModel(self, character, i=-1, j=-1):
		if (character.isdigit()):
			return MapCell(QuestionBlock(self.parent, 'COIN', int(character)))
		elif (character == 'P'):
			return MapCell(QuestionBlock(self.parent, 'MUSHROOM', 1))
		elif (character == 'E'):
			return MapCell(PipeBlock(self.parent))
		elif (character == '='):
			return MapCell(GroundBlock(self.parent))
		elif (character == 'X'):
			return MapCell(Block(self.parent))
		elif (character == 'U'):
			return MapCell(Block(self.parent).resetTexture("models/levels/objects/unbreakable_block.png"))
		elif (character == 'G'):
			return MapCell(AirBlock(self.parent), Enemy(self.parent, 0, str(i) + str(j) + "Goomba"))
		elif (character == 'T'):
			return MapCell(AirBlock(self.parent), Turtle(self.parent, 0, str(i) + str(j) + "Turtle"))
		elif (character == 'M'):
			return MapCell(AirBlock(self.parent))
		elif (character == 'C'):
			return MapCell(AirBlock(self.parent), items=Item(self.parent))
		else:
			return MapCell(AirBlock(self.parent))
	'''
		Parses the level attribute by name
		Returns the width and height of the level specified in the .lvl file
		
		file -- the file object for the level to be parsed
	'''
	def __findLevelAttr(self, file):
		height = 0
		width = 0
		last_pos = file.tell()
		
		line = file.readline()
		while line[0] == "#":
			if (len(line) > 2):
				attr_arr = line.split('#')[1]
				attr_arr = attr_arr.split('=')
				if (len(attr_arr) > 1):
					if (attr_arr[0].strip() == self.HEIGHT):
						height = int(attr_arr[1].strip())
					elif (attr_arr[0].strip() == self.WIDTH):
						width = int(attr_arr[1].strip())
			
			last_pos = file.tell()
			line = file.readline()
		
		file.seek(last_pos)
		return (width, height)
		
	'''
		Destroys all the elements in the map so that the map can be recreated
	'''
	def destroyMap(self):
		self.isHidden = True
		if self.map is not None:
			for col in self.map:
				for row in col:
					for enemy in row.enemies:
						enemy.destroy()
					for item in row.items:
						item.destroy()
						
					if(row.block.type is not 'AIR'):
						row.block.destroy()
			self.map = None
		self.backgroundModel.removeNode();
		
		
		